﻿///<FileHeader>
///     <FileName>RolePlayerDescriptor.cs</FileName>
///     <Author>Kiran Chand Palakkattiri</Author>
///     <Project>
///         <Name>Property Visibility Extensions for DSL</Name>
///         <Url>http://kiranchand.codeplex.com</Url>
///     </Project>
///     <CreationDate>September 07, 2009.</CreationDate>
///     <Contents>
///         <Content type="class" name="RolePlayerDescriptor"
///             baseclass="RolePlayerPropertyDescriptor"
///             comments="Visibility decorator for the advanced type decorator"/>
///     </Contents>
///     <Dependencies>
///         <Dependency type="reference" namespace="System" />
///         <Dependency type="reference" namespace="System.Collections.Generic" />
///         <Dependency type="reference" namespace="Microsoft.VisualStudio.Modeling"
///             assembly="Microsoft.VisualStudio.Modeling.Sdk" version="9.0.0.0" prerequisites="Visual Studio SDK 1.1"/>
///         <Dependency type="reference" namespace="Microsoft.VisualStudio.Modeling.Design"
///             assembly="Microsoft.VisualStudio.Modeling.Sdk" version="9.0.0.0" prerequisites="Visual Studio SDK 1.1" />
///     </Dependencies>
///     <Modifications/>
///     <Version>1.0.0.0</Version>
///</FileHeader>
using System;
using System.Collections.Generic;
using Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.Modeling.Design;
using System.Linq;
using Microsoft.VisualStudio.Modeling.Immutability;


namespace Kiranchand.Dsl.Extensions.PropertyVisibility
{
    /// <summary>
    /// Extended role player property descriptor for DSL role players supports dynamic property visibility.
    /// </summary>
    public class RolePlayerDescriptor : RolePlayerPropertyDescriptor
    {
        /// <summary>
        /// Gets or sets the instance of the role player decorator
        /// </summary>
        private IRolePlayerProviderDecorator RolePlayerDecorator
        {
            get
            {
                return this.GetFieldValue("describedElement") as IRolePlayerProviderDecorator;
            }
            set
            {
                if (value is IRolePlayerProviderDecorator
                    && value is ModelElement)
                {
                    this.SetFieldValue("describedElement", value);
                }
            }
        }

        /// <summary>
        /// Creates a new instance of the RolePlayerDescriptor
        /// </summary>
        /// <param name="sourcePlayer">the current model element</param>
        /// <param name="domainRole">the current domain role info</param>
        /// <param name="sourceDomainRoleInfoAttributes">the array of the current domain role attributes</param>
        public RolePlayerDescriptor(ModelElement sourcePlayer, DomainRoleInfo domainRole, Attribute[] sourceDomainRoleInfoAttributes)
            : base(sourcePlayer, domainRole, sourceDomainRoleInfoAttributes)
        {

        }      

        /// <summary>
        /// Get a list of the valid role players for this property value of the specified component
        /// </summary>
        /// <param name="component">The represented element, or null to use the stored represented element when in instance mode</param>
        /// <returns>List of ModelElements that could be chosen as the role player for the property</returns>
        /// <remarks>This is overridden in PVE2.0 instead of the ElementList property for IRolePlayerProvider interfacing.</remarks>
        protected override IList<ModelElement> BuildElementList(object component)
        {
            // get the opposite roleplayer element list 
            IList<ModelElement> elementList = this.BuildElementList(component, true);

            // check whether the component is role player decorator
            if (this.RolePlayerDecorator != null)
            {
                // iterate
                for (int i = 0; i < elementList.Count; i++)
                {
                    // can show on ui?
                    if (!this.RolePlayerDecorator.CanShowRolePlayer(this.DomainRoleInfo.RolePlayer
                        , elementList[i]))
                    {
                        // remove
                        elementList.RemoveAt(i);
                        // decrement loop
                        i--;
                    }
                }               
            }            

            // return the final collection
            return elementList;
        }


        // code for reference. not to be used any more!
        /// <summary>
        /// Build a list of the valid role players for this property value of the specified component.
        /// </summary>
        /// <param name="component">The represented element, or null to use the stored represented element when in instance mode</param>
        /// <param name="includeDescendents">Whether to include derived types for the domain role</param>
        /// <returns>List of ModelElements that could be chosen as the role player for the property</returns>
        /// <remarks>Uses method hiding from the base class here.</remarks>
        protected new IList<ModelElement> BuildElementList(object component, bool includeDescendents)
        {
            #region This code is copied from reflector; planning the following refactoring.
            /*
             * 1. Combine different predicates and execute them in single iteration
             * 2. This function has more than one point of return. This is the violation of
             * the standard "A function must have only one entry and only one exit."
             */ 

            #endregion

            // predicate
            Func<ModelElement, bool> existingRolePlayerCondition = null;

            // return collection
            List<ModelElement> elementList = new List<ModelElement>();

            // get the existing role player
            ModelElement existingRolePlayer = this.GetType().BaseType.Execute<ModelElement>(this
                , "GetExistingRolePlayer"
                , component);

            // null check
            if (existingRolePlayer != null)
                elementList.Add(existingRolePlayer);

            // whether can potentially change role player?
            if (this.GetType().BaseType.Execute<bool>(this
                , "CanPotentiallyChangeRolePlayer"
                , component))
            {
                // get the domain role
                DomainRoleInfo domainRoleInfo = this.GetType().BaseType.Execute<DomainRoleInfo>(this
                    , "GetDomainRoleInfo"
                    , component);

                // get the role player
                DomainClassInfo rolePlayer = domainRoleInfo.RolePlayer;
                // get the source
                IEnumerable<ModelElement> source = this.GetStore(component).ElementDirectory.FindElements(rolePlayer
                    , includeDescendents);

                // null check!
                if (existingRolePlayer != null)
                {
                    // instantiation of predicate; if null
                    if (existingRolePlayerCondition == null)
                        existingRolePlayerCondition = modelElement => modelElement != existingRolePlayer;

                    // find match
                    source = source.Where<ModelElement>(existingRolePlayerCondition);
                }
                if (domainRoleInfo.IsSource)
                {
                    // source role
                    source = source.Where<ModelElement>(candidate => !candidate.IsLocked(Locks.RolePlayer));

                    // operational link
                    if (this.GetType().BaseType.Execute<ElementLink>(this
                            , "CalculateOperationalLink"
                            , component) == null)
                    {
                        // locked filter 
                        source = source.Where<ModelElement>(candidate => !candidate.Partition.IsLocked(Locks.Add));
                    }
                }
                else
                {
                    // get the described element
                    this.GetDescribedElement(component);
                }
                // is one
                if (domainRoleInfo.IsOne)
                {
                    // iteration
                    foreach (ModelElement element in source)
                    {
                        // get the single roleplayer link
                        ElementLink existingSingleRolePlayerLink = this.GetType().BaseType.Execute<ElementLink>(this
                            , "GetExistingSingleRolePlayerLink"
                            , element);

                        // not null & can delete
                        if (existingSingleRolePlayerLink == null
                            || existingSingleRolePlayerLink.CanDelete(new Guid[0]))
                        {
                            // add
                            elementList.Add(element);
                        }
                    }
                    // return
                    return elementList;
                }
                // add source
                elementList.AddRange(source);
            }
            // return
            return elementList;
        }
    }
}
